package org.windowkit.toolkit.widget.tree;

import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Polygon;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.ArrayList;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.UIManager;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;


/**
 *
 * @author Eric Lundin
 */
public class TwoTreePane extends JComponent {

	/**
	 *
	 */
	private static final long serialVersionUID = -3495522471701008291L;

	/**
	 *
	 */
	private static final String TRANSFER_ALL_NODES = "Transfer All Nodes";

	/**
	 *
	 */
	private static final String TRANSFER_SELECTED_NODES = "Transfer Selection Nodes";

	/**
	 *
	 */
	private static final String REMOVE_SELECTED_NODES = "Remove Selected Nodes";

	/**
	 *
	 */
	private static final String REMOVE_ALL_NODES = "Remove All Nodes";

	public static final int LEFT_TREE = 0;

	public static final int RIGHT_TREE = 1;

	private static final int SPACING_SIZE = 5;

	private DropTarget rightDropTarget;

	private DropTarget leftDropTarget;

	private DragSource rightDragSource;

	private DragSource leftDragSource;

	// The left hand tree or "source" tree
	private TreePane leftTree;

	// The right hand tree or "destination" tree
	private TreePane rightTree;

	// Button to allow user to transfer the selected items.
	private JButton transferSelection;

	// Button to allow user to transfer all items.
	private JButton transferAll;

	// Button to allow user to remove selected items.
	private JButton removeSelection;

	// Button to allow user to remove all items.
	private JButton removeAll;

	// Stores this components unique ID.
	private String componentID = null;

	// The transfer handler for the left tree.
	// TwoTreeTransferHandler transferHandler;

	private DragSourceListener dragSourceListener;

	private JPanel buttonPanel;

	private ActionListener twoTreeActionListener;

	private TwoTreeSelectionListener twoTreeSelectionListener;

	/**
	 *
	 */
	public TwoTreePane(final TreeNode leftRoot, final String leftTreeTitle,
			final TreeNode rightRoot, final String rightTreeTitle) {
		super();
		init(leftRoot, rightRoot, leftTreeTitle, rightTreeTitle);
	}

	/**
	 * Initialize the two trees
	 */
	private void init(final TreeNode leftRoot, final TreeNode rightRoot,
			final String leftTreeTitle, final String rightTreeTitle) {
		initComponents(leftRoot, rightRoot);
		addComponents();

		rightTree.setBorder(BorderFactory.createTitledBorder(rightTreeTitle));
		leftTree.setBorder(BorderFactory.createTitledBorder(leftTreeTitle));
		rightTree
				.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
		leftTree
				.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
		twoTreeSelectionListener = new TwoTreeSelectionListener();
		leftTree.addTreeSelectionListener(twoTreeSelectionListener);
		rightTree.addTreeSelectionListener(twoTreeSelectionListener);
		// initDragAndDrop();
	}

	/**
	 * Initialize the drag and drop listeners and components. Currently
	 * commented out. May uncomment and make work later.
	 */
	private void initDragAndDrop() {
		// rightDropTarget = new DropTarget(rightTree.getTree(),
		// new TwoTreeDropTargetListener());
		// leftDropTarget = new DropTarget(leftTree.getTree(),
		// new TwoTreeDropTargetListener());
		// DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(
		// leftTree.getTree(), DnDConstants.ACTION_MOVE,
		// new TwoTreeDragGestureListener());
		// DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(
		// rightTree.getTree(), DnDConstants.ACTION_MOVE,
		// new TwoTreeDragGestureListener());
	}

	/**
	 * Add the components which make up this field.
	 */
	private void addComponents() {
		GridBagConstraints gbc = new GridBagConstraints();
		setLayout(new GridBagLayout());
		gbc.weightx = 5;
		gbc.fill = GridBagConstraints.BOTH;
		add(leftTree, gbc);
		gbc = new GridBagConstraints();
		gbc.weightx = 1;
		gbc.fill = GridBagConstraints.NONE;
		add(buttonPanel, gbc);
		gbc = new GridBagConstraints();
		gbc.weightx = 5;
		gbc.fill = GridBagConstraints.BOTH;
		add(rightTree, gbc);
	}

	/**
	 * Initialize the various components which make up TwoTreePane
	 *
	 * @param leftRoot
	 *            The left root node.
	 * @param rightRoot
	 *            The right root node.
	 */
	private void initComponents(final TreeNode leftRoot, final TreeNode rightRoot) {
		// Determine the icon size based on the font size.
		int iconSize = ((Font) UIManager.get("Button.font")).getSize();

		// Initialize the tree panes themselves.
		leftTree = new TreePane(leftRoot);
		rightTree = new TreePane(rightRoot);

		// Initialize the buttons with the correct arrow icons.
		transferSelection = new JButton(new ArrowIcon(ArrowIcon.SINGLE_RIGHT,
				iconSize));
		transferAll = new JButton(new ArrowIcon(ArrowIcon.DOUBLE_RIGHT,
				iconSize));
		removeSelection = new JButton(new ArrowIcon(ArrowIcon.SINGLE_LEFT,
				iconSize));
		removeAll = new JButton(new ArrowIcon(ArrowIcon.DOUBLE_LEFT, iconSize));

		// Set tool tip text for the buttons.
		transferSelection.setToolTipText("Add selected item(s)");
		transferAll.setToolTipText("Add all item(s)");
		removeSelection.setToolTipText("Remove selected item(s)");
		removeAll.setToolTipText("Remove all items");

		// Set action commands for the buttons so they will be easier to
		// identify.
		transferSelection.setActionCommand(TRANSFER_SELECTED_NODES);
		transferAll.setActionCommand(TRANSFER_ALL_NODES);
		removeSelection.setActionCommand(REMOVE_SELECTED_NODES);
		removeAll.setActionCommand(REMOVE_ALL_NODES);

		// Initialize and add action listeners to the buttons.
		twoTreeActionListener = new TwoTreeActionListener();
		transferSelection.addActionListener(twoTreeActionListener);
		transferAll.addActionListener(twoTreeActionListener);
		removeSelection.addActionListener(twoTreeActionListener);
		removeAll.addActionListener(twoTreeActionListener);

		// Disable buttons until a selection is made.
		transferSelection.setEnabled(false);
		// transferAll.setEnabled(false);
		removeSelection.setEnabled(false);
		// removeAll.setEnabled(false);

		// Add the buttons to a panel.
		buttonPanel = new JPanel();
		buttonPanel.setLayout(new GridLayout(4, 1, SPACING_SIZE, SPACING_SIZE));
		buttonPanel.add(transferSelection);
		buttonPanel.add(transferAll);
		buttonPanel.add(removeSelection);
		buttonPanel.add(removeAll);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see com.bmi.foundation.core.SComponent#getIdentifier()
	 */

	public String getIdentifier() {
		return componentID;
	}

	/**
	 * Convienience method to retrieve a certain tree without the need for if
	 * statements. Throws illegal argument exception if the side argument is not
	 * TwoTreePane.LEFT_TREE or TwoTreePane.RIGHT_TREE.
	 *
	 * @param side
	 *            THe side to retreive use either TwoTreePane.LEFT_TREE or
	 *            TwoTreePane.RIGHT_TREE
	 * @return The specified treepane.
	 */
	public TreePane getTreePane(final int side) {
		checkLegalSide(side);
		if (side == RIGHT_TREE) {
			return rightTree;
		}
		else {
			return leftTree;
		}
	}

	/**
	 * Clears both trees leaving only the root node.
	 */
	public void clear() {
		((DefaultMutableTreeNode) leftTree.getTree().getModel().getRoot())
				.removeAllChildren();
		((DefaultTreeModel) leftTree.getTree().getModel()).reload();
		((DefaultMutableTreeNode) rightTree.getTree().getModel().getRoot())
				.removeAllChildren();
		((DefaultTreeModel) rightTree.getTree().getModel()).reload();
	}

	/**
	 * Transfers the selected node from the tree specified to the other tree.
	 * Use either TwoTreePane.LEFT_TREE or TwoTreePane.RIGHT_TREE.
	 *
	 * @param source
	 *            The tree to move the selected nodes from. Use either
	 *            TwoTreePane.LEFT_TREE or TwoTreePane.RIGHT_TREE.
	 */
	private void moveSelectedNodes(final int source) {
		// TODO Auto-generated method stub
		checkLegalSide(source);
		BaseDefaultMutableTreeNode[] selectedNodes = getTreePane(
				source).getSelectedNodes();
		if (selectedNodes.length < 1) {
			return;// NO-OP
		}
		transferNode(selectedNodes[0], source == LEFT_TREE ? true : false);
	}

	/**
	 * Moves all nodes from the given tree to the other tree.
	 *
	 * @param source
	 *            The tree to move the nodes from Must be one of
	 *            TwoTreePane.LEFT_SIDE or TwoTreePane.RIGHT_SIDE.
	 */
	private void moveAllNodes(final int source) {
		checkLegalSide(source);
		BaseDefaultMutableTreeNode root = (BaseDefaultMutableTreeNode) getTreePane(
				source).getModel().getRoot();
		// Can't use enumeration here since modifying the root's child
		// collection invalidates the enumeration.
		while (root.getChildCount() > 0) {
			transferNode((BaseDefaultMutableTreeNode) root.getChildAt(0),
					source == LEFT_TREE ? true : false);
		}
	}

	/**
	 * Determines which level to transfer a node at. For instance if
	 * transferring a given leaf node would leave a branch node with no children
	 * we should transfer the branch node as well.
	 *
	 * @param node
	 *            The node to consider transfering.
	 * @return The node/branch to actually transfer.
	 */
	private DefaultMutableTreeNode findNodeToTransfer(
			final DefaultMutableTreeNode node) {
		DefaultMutableTreeNode parentNode;
		if (node.isRoot()) {
			return node;
		}

		parentNode = (DefaultMutableTreeNode) node.getParent();
		if (parentNode.getChildCount() <= 2) {
			return findNodeToTransfer(parentNode);
		}
		return node;
	}

	/**
	 * Transfers the given node in the specified direction.
	 *
	 * @param node
	 *            The node to transfer.
	 * @param add
	 *            True if this is a left to right transfer. False if it is a
	 *            right to left transfer.
	 */
	private void transferNode(final BaseDefaultMutableTreeNode node, final boolean add) {
		TreePane sourceTree;
		TreePane destTree;

		if (add) {
			sourceTree = leftTree;
			destTree = rightTree;
		} else {
			sourceTree = rightTree;
			destTree = leftTree;
		}
		debugTester(sourceTree);
		if (!node.isLeaf()
				&& (destTree.findNodeByDisplayValue(node.getDisplayValue()) != null)) {
			node.getChildCount();
			while (node.getChildCount() > 0) {
				transferNode((BaseDefaultMutableTreeNode) node.getChildAt(0), add);
			}
			sourceTree.removeNode(sourceTree.findNodeByDisplayValue(node
					.getDisplayValue()));
		}
		else {
			transferChild(node, sourceTree, destTree);
		}
	}

	private void transferChild(final BaseDefaultMutableTreeNode node,
			final TreePane sourceTree, final TreePane destTree) {
		node.getDisplayValue();
		TreePath pathToNode;
		BaseDefaultMutableTreeNode transferNode = null;
		BaseDefaultMutableTreeNode destinationNode = null;
		BaseDefaultMutableTreeNode tempNode;
		BaseDefaultMutableTreeNode nodeToMove;
		pathToNode = new TreePath(node.getPath());


		for (int i = 1; i < pathToNode.getPathCount(); i++) {
			if ((tempNode = destTree
					.findNodeByDisplayValue(((BaseDefaultMutableTreeNode) ((BaseDefaultMutableTreeNode) pathToNode
							.getPathComponent(i)).getParent())
							.getDisplayValue())) != null) {
				destinationNode = tempNode;
				transferNode = (BaseDefaultMutableTreeNode) pathToNode
						.getPathComponent(i);
			}
		}
		nodeToMove = buildBranch(transferNode, destinationNode, node,
				sourceTree);
		destTree.addBranch(nodeToMove, destinationNode);
		destinationNode.sortChildren();
		int[] childIndicies = new int[destinationNode.getChildCount()];
		for (int i = 0; i < childIndicies.length; i++) {
			childIndicies[i] = i;
		}
		destTree.getModel().nodesChanged(destinationNode, childIndicies);
	}

	private void debugTester(final TreePane sourceTree) {
		// Debug code remove
		System.out.println("TEST");
		for (int i = 0; i < sourceTree.getTree().getRowCount(); i++) {
			if (sourceTree
					.findNodeByDisplayValue(((BaseDefaultMutableTreeNode) sourceTree
							.getTree().getPathForRow(i).getLastPathComponent())
							.getDisplayValue()) == null) {
				System.out.println("fail"
						+ ((BaseDefaultMutableTreeNode) sourceTree.getTree()
								.getPathForRow(i).getLastPathComponent())
								.getDisplayValue());
			}
		}
		System.out.println("TEST");
	}

	/**
	 * Builds the branch to be transfered over.
	 *
	 * @param transferNode
	 *            The node which needs to be transfered.
	 * @param destinationNode
	 *            THe parent which will be added.
	 * @param node
	 *            The node which was selected.
	 * @param sourceTree
	 *            The tree the branch will be added to.
	 * @return The root node of this branch.
	 */
	BaseDefaultMutableTreeNode buildBranch(final BaseDefaultMutableTreeNode transferNode,
			final BaseDefaultMutableTreeNode destinationNode,
			final BaseDefaultMutableTreeNode node, final TreePane sourceTree) {
		TreePath nodePath = new TreePath(node.getPath());
		ArrayList<BaseDefaultMutableTreeNode> tempArray = new ArrayList<BaseDefaultMutableTreeNode>();

		for (int i = destinationNode.getPath().length; i < node.getPath().length; i++) {
			if (((BaseDefaultMutableTreeNode) nodePath.getPathComponent(i))
					.getDisplayValue().equals(node.getDisplayValue())) {
				tempArray
						.add(((BaseDefaultMutableTreeNode) nodePath
								.getPathComponent(i)));
				System.out.println("No clone");
			} else {
				System.out.println("clone");
				tempArray
						.add((BaseDefaultMutableTreeNode) ((BaseDefaultMutableTreeNode) nodePath
								.getPathComponent(i)).clone());
			}
			if (((BaseDefaultMutableTreeNode) nodePath.getPathComponent(i))
					.getChildCount() <= 1
					|| ((BaseDefaultMutableTreeNode) nodePath.getPathComponent(i))
							.getDisplayValue().equals(node.getDisplayValue())) {
				sourceTree
						.removeNode(sourceTree
								.findNodeByDisplayValue(((BaseDefaultMutableTreeNode) nodePath
										.getPathComponent(i)).getDisplayValue()));
			}
		}

		for (int i = 0; i < tempArray.size() - 1; i++) {
			tempArray.get(i).add(tempArray.get(i + 1));
		}
		return tempArray.get(0);
	}

	/**
	 * Set the popup menu to use with a tree.
	 *
	 * @param menu
	 *            The menu to use.
	 * @param side
	 *            The side to assign this menu to. Use TwoTreePane.LEFT_TREE
	 *            and TwoTreePane.RIGHT_TREE.
	 */
	public void setPopupMenu(final JPopupMenu menu, final int side) {
		getTreePane(side).setPopupMenu(menu);
	}

	/**
	 * Sets which node is selected on the given tree. Use TwoTreePane.LEFT_TREE
	 * or TwoTreePane.RIGHT_TREE.
	 *
	 * @param node
	 *            The node to select.
	 * @param side
	 *            The side to select this node on.
	 */
	public void setSelectedNode(final BaseDefaultMutableTreeNode node, final int side) {
		checkLegalSide(side);
		getTreePane(side).setSelectedNodes(node);
	}

	@Override
	public void setEnabled(final boolean isEnabled) {
		leftTree.setEnabled(isEnabled);
		rightTree.setEnabled(isEnabled);
		transferSelection.setEnabled(isEnabled);
		transferAll.setEnabled(isEnabled);
		removeSelection.setEnabled(isEnabled);
		removeAll.setEnabled(isEnabled);
	}

	/**
	 * @param side
	 */
	private void checkLegalSide(final int side) {
		if (side != LEFT_TREE && side != RIGHT_TREE) {
			throw new IllegalArgumentException(
					"TwoTreePane: Must use either TwoTreePane.LEFT_TREE or TwoTreePane.LEFT_TREE");
		}
	}

	public class ArrowIcon implements Icon {
		// Signifies a single right arrow should be drawn.
		public static final int SINGLE_RIGHT = 0;

		// Signifies a single left arrow should be drawn.
		public static final int SINGLE_LEFT = 1;

		// Signifies a double right arrow should be drawn.
		public static final int DOUBLE_RIGHT = 2;

		// Signifies a double left arrow should be drawn.
		public static final int DOUBLE_LEFT = 3;

		// Stores the type of arrow to draw.
		private final int type;

		// Stores the size of this icon
		private final int size;

		public ArrowIcon(final int type, final int size) {
			this.type = type;
			this.size = size;
		}

		/*
		 * (non-Javadoc)
		 *
		 * @see javax.swing.Icon#paintIcon(java.awt.Component,
		 *      java.awt.Graphics, int, int)
		 */
		public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
			int halfSize = (size / 2);
			Polygon triangle = new Polygon();
			boolean componentEnabled = c.isEnabled();
			if (type == SINGLE_RIGHT) {
				triangle.addPoint(x, y);
				triangle.addPoint(x, y + size);
				triangle.addPoint(x + size, y + halfSize);
				paintTriangle(triangle, g, componentEnabled);
			} else if (type == SINGLE_LEFT) {
				triangle.addPoint(x + size, y);
				triangle.addPoint(x + size, y + size);
				triangle.addPoint(x, y + halfSize);
				paintTriangle(triangle, g, componentEnabled);
			} else if (type == DOUBLE_RIGHT) {
				triangle.addPoint(x, y);
				triangle.addPoint(x, y + size);
				triangle.addPoint(x + size, y + halfSize);
				triangle.translate(-halfSize, 0);
				paintTriangle(triangle, g, componentEnabled);
				triangle.translate(size, 0);
				paintTriangle(triangle, g, componentEnabled);
			} else if (type == DOUBLE_LEFT) {
				triangle.addPoint(x + size, y);
				triangle.addPoint(x + size, y + size);
				triangle.addPoint(x, y + halfSize);
				triangle.translate(-halfSize, 0);
				paintTriangle(triangle, g, componentEnabled);
				triangle.translate(size, 0);
				paintTriangle(triangle, g, componentEnabled);
			}
		}

		public TreePane getLeftTree() {
			return leftTree;
		}

		public TreePane getRightTree() {
			return rightTree;
		}

		/**
		 * Paints a shadow with a "shadow" which is simple its outline.
		 *
		 * @param shape
		 *            The shape to paint.
		 * @param g
		 *            The graphics object to draw on.
		 */
		private void paintTriangle(final Polygon shape, final Graphics g, final boolean enabled) {
			if (enabled) {
				g.setColor(UIManager.getColor("Button.foreground"));
			}
			else {
				g.setColor(UIManager.getColor("Button.disabledForeground"));
			}
			g.fillPolygon(shape);
			if (enabled) {
				g.setColor(UIManager.getColor("Button.shadow"));
			}
			else {
				g.setColor(UIManager.getColor("Button.disabledShadow"));
			}
			g.drawPolygon(shape);
		}

		/*
		 * (non-Javadoc)
		 *
		 * @see javax.swing.Icon#getIconWidth()
		 */
		public int getIconWidth() {
			return size;
		}

		/*
		 * (non-Javadoc)
		 *
		 * @see javax.swing.Icon#getIconHeight()
		 */
		public int getIconHeight() {
			return size;
		}
	}

	// Partial drag and drop functionality. Now that transferring branches works
	// this can easily b
	/*
	 * private class TwoTreeDropTargetListener implements DropTargetListener {
	 * (non-Javadoc)
	 *
	 * @see java.awt.dnd.DropTargetListener#drop(java.awt.dnd.DropTargetDropEvent)
	 *      public synchronized void drop(DropTargetDropEvent
	 *      dropTargetDropEvent) { } } public void dragOver(DropTargetDragEvent
	 *      dtde) { } (non-Javadoc)
	 * @see java.awt.dnd.DropTargetListener#dragEnter(java.awt.dnd.DropTargetDragEvent)
	 *      public void dragEnter(DropTargetDragEvent dtde) { } (non-Javadoc)
	 * @see java.awt.dnd.DropTargetListener#dropActionChanged(java.awt.dnd.DropTargetDragEvent)
	 *      public void dropActionChanged(DropTargetDragEvent dtde) { // TODO
	 *      Auto-generated method stub } (non-Javadoc)
	 * @see java.awt.dnd.DropTargetListener#dragExit(java.awt.dnd.DropTargetEvent)
	 *      public void dragExit(DropTargetEvent dte) { } } public class
	 *      TwoTreeDragGestureListener implements DragGestureListener {
	 *      (non-Javadoc)
	 * @see java.awt.dnd.DragGestureListener#dragGestureRecognized(java.awt.dnd.DragGestureEvent)
	 *      public synchronized void dragGestureRecognized(DragGestureEvent e) { } } }
	 *      private static class TwoTreeDragSourceListener implements
	 *      DragSourceListener { public void dragDropEnd(DragSourceDropEvent
	 *      dragSourceDropEvent) { } public void dragEnter(DragSourceDragEvent
	 *      dragSourceDragEvent) { } public void dragExit(DragSourceEvent
	 *      dragSourceEvent) { } public void dragOver(DragSourceDragEvent
	 *      dragSourceDragEvent) { } public void
	 *      dropActionChanged(DragSourceDragEvent dragSourceDragEvent) { } }
	 */

	class TwoTreeSelectionListener implements TreeSelectionListener {

		/*
		 * (non-Javadoc)
		 *
		 * @see javax.swing.event.TreeSelectionListener#valueChanged(javax.swing.event.TreeSelectionEvent)
		 */
		public void valueChanged(final TreeSelectionEvent e) {
			Tree source = (Tree) e.getSource();
			boolean enable = enableButtons(source);
			if (source == leftTree.getTree()) {
				transferSelection.setEnabled(enable);
			} else if (source == rightTree.getTree()) {
				removeSelection.setEnabled(enable);
			}
		}

		private boolean enableButtons(final Tree source) {
			return (source.getSelectionCount() >= 1);
		}

	}

	class TwoTreeTransferable extends BaseDefaultMutableTreeNode implements
			Transferable {

		/**
		 *
		 */
		private static final long serialVersionUID = -4865086737713328316L;

		public DataFlavor SDEFAULT_MUTABLE_TREE_NODE_FLAVOR = new DataFlavor(
				org.windowkit.toolkit.widget.tree.BaseDefaultMutableTreeNode.class,
				"BaseDefaultMutableTreeNode");

		BaseDefaultMutableTreeNode treeNode;

		/**
		 *
		 */
		public TwoTreeTransferable(final BaseDefaultMutableTreeNode treeNode) {
			super(treeNode.getDisplayValue(), treeNode
					.getUserObjectCollection(), treeNode.getChildren(),
					(MutableTreeNode) treeNode.getParent());
			this.treeNode = treeNode;
		}

		/*
		 * (non-Javadoc)
		 *
		 * @see java.awt.datatransfer.Transferable#getTransferDataFlavors()
		 */
		public DataFlavor[] getTransferDataFlavors() {
			return new DataFlavor[] { SDEFAULT_MUTABLE_TREE_NODE_FLAVOR };
		}

		/*
		 * (non-Javadoc)
		 *
		 * @see java.awt.datatransfer.Transferable#isDataFlavorSupported(java.awt.datatransfer.DataFlavor)
		 */
		public boolean isDataFlavorSupported(final DataFlavor flavor) {
			if (flavor.equals(SDEFAULT_MUTABLE_TREE_NODE_FLAVOR)) {
				return true;
			}
			else {
				return false;
			}
		}

		/*
		 * (non-Javadoc)
		 *
		 * @see java.awt.datatransfer.Transferable#getTransferData(java.awt.datatransfer.DataFlavor)
		 */
		public Object getTransferData(final DataFlavor flavor)
				throws UnsupportedFlavorException, IOException {
			if (isDataFlavorSupported(flavor)) {
				if (treeNode == null) {
					throw new IOException("Nothing to drop");
				}
				else {
					return treeNode;
				}
			}
			else {
				throw new UnsupportedFlavorException(flavor);
			}
		}
	}

	public class TwoTreeActionListener implements ActionListener {
		public void actionPerformed(final ActionEvent e) {
			Object source = e.getSource();
			if (source instanceof JButton) {
				String actionCommand = ((JButton) source).getActionCommand();
				if (actionCommand.equals(TRANSFER_SELECTED_NODES)) {
					moveSelectedNodes(LEFT_TREE);
				} else if (actionCommand.equals(TRANSFER_ALL_NODES)) {
					moveAllNodes(LEFT_TREE);
				} else if (actionCommand.equals(REMOVE_SELECTED_NODES)) {
					moveSelectedNodes(RIGHT_TREE);
				} else if (actionCommand.equals(REMOVE_ALL_NODES)) {
					moveAllNodes(RIGHT_TREE);
				}
			}

		}
	}


	public void setIdentifier(final String componentID) {
		this.componentID = componentID;
		// TODO broked
//		leftTree.setIdentifier(componentID + ".leftTree");
//		rightTree.setIdentifier(componentID + ".rightTree");
	}

}
